﻿using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace SearchForWorkitemsChanges
{
    /// <summary>
    /// Class that implements <see cref="ITfsInfoProvider"/> contracts for the Team Explorer Cache
    /// </summary>
    internal class TeamExplorerCacheInfoProvider : VmBase<TeamExplorerCacheInfoProvider>, ITfsInfoProvider
    {
        /// <summary>
        /// Chooses a new team project collection.
        /// That will induce the changes of <see cref="CurrentProject" />, <see cref="CurrentCollection" />.
        /// The event <see cref="INotifyPropertyChanged" />.PropertyChanged will be called during the changes.
        /// </summary>
        public void ChooseNewTeamProjectCollection()
        {
            using (TeamProjectPicker tpp = new TeamProjectPicker(TeamProjectPickerMode.SingleProject, false))
            {
                DialogResult result = tpp.ShowDialog();
                if (result == DialogResult.OK)
                {
                    System.Console.WriteLine("Selected Team Project Collection Uri: " + tpp.SelectedTeamProjectCollection.Uri);
                    System.Console.WriteLine("Selected Projects:");

                    this.CurrentCollection = tpp.SelectedTeamProjectCollection;


                    foreach (ProjectInfo projectInfo in tpp.SelectedProjects)
                        this.CurrentProject = projectInfo;

                }
                else
                {
                    this.CurrentProject = null;
                    this.CurrentCollection = null;
                }
            }
        }

        private Microsoft.TeamFoundation.WorkItemTracking.Client.Project _currentProjectWorkItem;
        private ProjectInfo _currentProject;

        /// <summary>
        /// Gets the current project. This property raise the event <see cref="INotifyPropertyChanged" />.PropertyChanged
        /// </summary>
        /// <value>
        /// The current project.
        /// </value>
        public ProjectInfo CurrentProject
        {
            get
            {
                return this._currentProject;
            }
            private set
            {
                this._currentProject = value; this.NotifyPropertyChanged(p => p.CurrentProject);
                if (this._currentProject == null)
                    this._currentProjectWorkItem = null;
                else
                {
                    var workItemProvider = this.CurrentCollection.GetService<Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore>();
                    foreach (Microsoft.TeamFoundation.WorkItemTracking.Client.Project project in workItemProvider.Projects)
                    {
                        if (project.Name == this.CurrentProject.Name)
                        {
                            this._currentProjectWorkItem = project;
                            break;
                        }
                    }
                }
            }
        }

        private TfsTeamProjectCollection _currentTeamCollection;

        /// <summary>
        /// Gets the current collection. This property raise the event <see cref="INotifyPropertyChanged" />.PropertyChanged
        /// </summary>
        /// <value>
        /// The current project.
        /// </value>
        public TfsTeamProjectCollection CurrentCollection
        {
            get
            {
                return this._currentTeamCollection;
            }
            private set
            {
                this._currentTeamCollection = value; this.NotifyPropertyChanged(p => p.CurrentCollection);
            }
        }

        /// <summary>
        /// Gets the change sets.
        /// </summary>
        /// <param name="from">From.</param>
        /// <param name="to">To.</param>
        /// <param name="basePath">The base path.</param>
        /// <returns></returns>
        public IEnumerable<Changeset> GetChangeSets(DateTime from, DateTime to, string basePath = null)
        {
            var rootPath = string.IsNullOrEmpty(basePath) ? String.Format("$/{0}/", this.CurrentProject.Name) : basePath;
            var serviceProvider = this.CurrentCollection.GetService<VersionControlServer>();
            return serviceProvider.QueryHistory(
                rootPath
                , VersionSpec.Latest
                , 0
                , RecursionType.Full
                , null
                , from.ConvertToVersionSpec()
                , to.AddDays(1).ConvertToVersionSpec()
                , Int32.MaxValue
                , true
                , false
                ).Cast<Changeset>().ToList();
        }

        ///// <summary>
        ///// Gets the branches.
        ///// </summary>
        ///// <returns></returns>
        //public IEnumerable<BranchObject> GetBranches()
        //{
        //    var projectRootPath = String.Format("$/{0}", this.CurrentProject.Name);
        //    var serviceProvider = this.CurrentCollection.GetService<VersionControlServer>();
        //    var items = serviceProvider.QueryRootBranchObjects(RecursionType.Full).Where(b => !b.Properties.RootItem.IsDeleted && b.Properties.RootItem.Item.StartsWith(projectRootPath));

        //    return items.ToList();
        //}

        /// <summary>
        /// Gets the folders.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Item> GetFolders(string basePath = null)
        {
            string projectRootPath;
            var serviceProvider = this.CurrentCollection.GetService<VersionControlServer>();
            Func<Item, bool> IsBasePath;
            RecursionType recursion;

            if (string.IsNullOrEmpty(basePath))
            {
                projectRootPath = String.Format("$/{0}", this.CurrentProject.Name);
                recursion = RecursionType.None;
                IsBasePath = (i) => false;
            }
            else
            {
                projectRootPath = basePath;
                recursion = RecursionType.OneLevel;
                IsBasePath = (i) => i.ServerItem == basePath;
            }

            var items = serviceProvider.GetItems(
                new ItemSpec(projectRootPath, recursion)
                , VersionSpec.Latest
                , DeletedState.NonDeleted
                , ItemType.Folder
                , GetItemsOptions.IncludeBranchInfo).Items
                .Where(i => !IsBasePath(i)).ToList();

            return items;
        }

        /// <summary>
        /// Get all work item types allowed in the <see cref="CurrentProject" />.
        /// </summary>
        /// <returns>
        /// An <see cref="IEnumerable{T}" /> where T is <see cref="string" />
        /// </returns>
        public IEnumerable<string> GetWorkItemTypes()
        {
            if (this._currentProjectWorkItem != null)
            {
                foreach (WorkItemType item in this._currentProjectWorkItem.WorkItemTypes)
                    yield return item.Name;
            }

            yield break;
        }
    }
}
